﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zocono.Infrastructure.Common;
using Zocono.WCS.Domain.BytesMapObjectDomain;
using Zocono.WCS.Domain.DomainRepository;
using Zocono.WCS.Domain.EntityInfo;
using Zocono.WCS.Domain.GlobalClass;
using Zocono.WCS.Domain.LogDomain;
using Zocono.WCS.Domain.PlcBytesObject;
using Zocono.WCS.Domain.PlcConnectPoolDomain;
using Zocono.WCS.Domain.ProcessModuleDomain;

namespace Zocono.WCS.Domain.JobExcutorDomain
{

    public class ReadDbJobService : IReadDbJobService
    {
        private readonly IRunLogInfoService _IRunLogInfoService;
        private readonly IPlcConnectionPoolService _plcConnectionPoolService;
        private readonly IBytesMapObjectService _bytesMapObjectService;
        private readonly IBytesMapObjectInfoRepository _bytesMapObjectInfoRepository;
        private readonly ILogUtil _logUtil;
        private readonly NlogUtilScoped _NlogUtilScoped;
        //交互报文内存
        private static readonly Dictionary<long, byte[]> _JiaohuBytesMap = new();
        //监控报文内存
        private readonly Dictionary<string, byte[]> _JiankongBytesMap = new();
        public ReadDbJobService(IPlcConnectionPoolService plcConnectionPoolService, IBytesMapObjectService bytesMapObjectService, ILogUtil logUtil
            , IRunLogInfoService runLogInfoService, IBytesMapObjectInfoRepository bytesMapObjectInfoRepository, NlogUtilScoped nlogUtilScoped)
        {
            _plcConnectionPoolService = plcConnectionPoolService;
            _bytesMapObjectService = bytesMapObjectService;
            _logUtil = logUtil;
            _IRunLogInfoService = runLogInfoService;
            _bytesMapObjectInfoRepository = bytesMapObjectInfoRepository;
            _NlogUtilScoped = nlogUtilScoped;
        }
        /// <summary>
        /// 监控专用读取plc数据并解析报文集合
        /// </summary>
        /// <param name="jobName"></param>
        /// <param name="jobPlcReadDb"></param>
        /// <param name="pargrame"></param>
        /// <returns></returns>
        public async Task<List<ProcessModule>> ReadPlanerPlcDbAsync(string jobName, PlcReadDbJobData jobPlcReadDb, string pargrame)
        {


            string key = $"{pargrame}_{jobName}";
            _NlogUtilScoped.Debug($",ReadPlanerPlcDbAsync_1:{key}");
            List<ProcessModule> processModules = new();
            if (jobPlcReadDb == null)
            {
                return processModules;
            }
            //1相同作业跟据不同PLC来读取
            var plcConnDbs = jobPlcReadDb.JobPlcDbs.GroupBy(p => p.PlcConnectionID);
            if (!GlobalResource.BytesMapObjectInfos.Any())
            {
                GlobalResource.BytesMapObjectInfos = await _bytesMapObjectInfoRepository.GetListAsync();
            }
            _NlogUtilScoped.Debug($"ReadPlanerPlcDbAsync2:{key}");
            //读报文
            foreach (var dbKey in plcConnDbs)
            {
                //dbKey一组相同的PlcConnectionID
                long plcConnectionId = dbKey.Key;
                var plcDbs = dbKey.AsEnumerable();

                try
                {
                    //插入或更新锁定作业
                    var plcConnectionS7 = _plcConnectionPoolService.S7GetReadPlcConnectionState(plcConnectionId, jobName, pargrame);
                    _NlogUtilScoped.Debug($"ReadPlanerPlcDbAsync3:{key}");
                    if (plcConnectionS7 == null)
                    {
                        continue;
                    }
                    foreach (var db in plcDbs)
                    {

                        _NlogUtilScoped.Debug($"ReadPlanerPlcDbAsync4,db:{db.DbCode},start:{db.DbStart},length:{db.DbLength},plcConnectionS7:{plcConnectionS7.IP}");
                        byte[] bytes = await plcConnectionS7.ReadDbForMonitorAsync(db.DbCode, db.DbStart, db.DbLength, _NlogUtilScoped);
                        _NlogUtilScoped.Debug($"ReadPlanerPlcDbAsync5:{key}|{db.EquipmentCode}");
                        if (bytes == null || bytes.Length <= 0)
                        {
                            _NlogUtilScoped.Error($"监控ReadPlcDbJob 读DB失败，jobid【{db.JobID}】，plcip【{plcConnectionS7.IP}】，plc名称【{db.PlcName}】，设备号【{db.EquipmentCode}】，modulName【{db.ProcessModuleName}】,modulType【{db.ProcessModuleType}】, DB:{db.DbCode},DbStart:{db.DbStart},DbLength:{db.DbLength}");
                            continue;
                        }
                        var bytesMapObjects = GlobalResource.BytesMapObjectInfos.FindAll(m => m.PlcReadDbId == db.ID);
                        var objects = await _bytesMapObjectService.ByteMapToObjectsMonitorAsync(plcConnectionId, db.ID, bytes, bytesMapObjects);
                        _NlogUtilScoped.Debug($"ReadPlanerPlcDbAsync6:{key}|{db.EquipmentCode}");
                        if (objects == null)
                        {
                            continue;
                        }

                        //初始化处理模块
                        ProcessModule processModule = null;

                        if (processModules.Exists(pm => pm.ProcessModuleName == db.ProcessModuleName && pm.ProcessModuleType == db.ProcessModuleType))
                        {
                            //即使有相同的也再复制一个相同的ProcessModule
                            processModule = processModules.Find(pm => pm.ProcessModuleName == db.ProcessModuleName && pm.ProcessModuleType == db.ProcessModuleType);
                        }
                        else
                        {
                            //没有相同则新增一个
                            processModule = new ProcessModule { ProcessModuleName = db.ProcessModuleName, ProcessModuleType = db.ProcessModuleType };
                            processModules.Add(processModule);
                        }
                        //按设备号设组报文//可以用Start分组。起始一样IP一样（能进来都是同一个IP这样就可以实现初始不需要设备号）
                        var equipmentGroup = objects.Where(l => l.EquipmentCode.Length == 6 && l.EquipmentCode.Length != 1).GroupBy(o => o.EquipmentCode);//归类

                        //设备分组报文添加到处理模型（在这里可以将WCS的设备复制，如果设备为空）
                        foreach (var equipmentDbObjs in equipmentGroup)
                        {
                            EquipmentDbObject equipmentDbObject = null;
                            if (processModule.equipmentDbObjects.Exists(eo => eo.EquipmentCode == equipmentDbObjs.Key))
                            {
                                equipmentDbObject = processModule.equipmentDbObjects.Find(eo => eo.EquipmentCode == equipmentDbObjs.Key);
                                //处理模块中已存在设备报文，追加
                                equipmentDbObject.bytesObjectBases.AddRange(equipmentDbObjs.AsEnumerable());
                            }
                            else
                            {
                                equipmentDbObject = new EquipmentDbObject { EquipmentCode = equipmentDbObjs.Key };
                                equipmentDbObject.bytesObjectBases.AddRange(equipmentDbObjs.AsEnumerable());
                                //处理模块中不存在设备报文，添加
                                processModule.equipmentDbObjects.Add(equipmentDbObject);
                            }
                        }
                        _NlogUtilScoped.Debug($"ReadPlanerPlcDbAsync7:{key}");
                    }

                }
                catch (Exception ex)
                {
                    _logUtil.Error($"监控ReadPlcDbJob 读DB失败读取PLC【{plcConnectionId}】时报错ex={ex.StackTrace}");
                }
            }

            _NlogUtilScoped.Debug($"ReadPlanerPlcDbAsync_8:{key}");
            return processModules;
        }

        /// <summary>
        /// 交互读取plc数据并解析返回报文集合
        /// </summary>
        /// <param name="jobName"></param>
        /// <param name="jobPlcReadDb"></param>
        /// <param name="programe"></param>
        /// <returns></returns>
        public async Task<List<ProcessModule>> ReadPlcDbAsync(string jobName, PlcReadDbJobData jobPlcReadDb, string programe)
        {

            List<ProcessModule> processModules = new();

            if (jobPlcReadDb == null)
            {
                return processModules;
            }
            //1相同作业跟据不同PLC来读取
            var plcConnDbs = jobPlcReadDb.JobPlcDbs.GroupBy(p => p.PlcConnectionID);
            if (!GlobalResource.BytesMapObjectInfos.Any())
            {
                GlobalResource.BytesMapObjectInfos = await _bytesMapObjectInfoRepository.GetListAsync();
            }

            // _logUtil.Debug($"ReadPlcDbAsync:{jobName},{programe}");
            //读报文
            foreach (var dbKey in plcConnDbs)
            {
                //dbKey一组相同的PlcConnectionID
                long plcConnectionId = dbKey.Key;
                var plcDbs = dbKey.AsEnumerable();

                try
                {
                    //插入或更新锁定作业
                    var plcConnectionS7 = _plcConnectionPoolService.S7GetReadPlcConnection(plcConnectionId, jobName, programe);
                    if (plcConnectionS7 == null)
                    {
                        continue;
                    }

                    foreach (var db in plcDbs)
                    {
                        //读Plc DB块内容
                        byte[] bytes = await plcConnectionS7.ReadDbAsync(db.DbCode, db.DbStart, db.DbLength);// new byte[] { 0, 6, 65, 145, 0, 0, 39, 193, 0, 6, 65, 157, 18, 10, 70, 65, 74, 71, 68, 48, 48, 48, 54, 49, 13, 32, 32, 32, 32, 32, 32, 32, 0, 0, 1, 2, 0, 0, 0, 0 };

                        if (bytes == null || bytes.Length <= 0)
                        {
                            _logUtil.Error($"交互ReadPlcDbJob 读DB失败，jobid【{db.JobID}】，plcip【{plcConnectionS7.IP}】，plc名称【{db.PlcName}】，设备号【{db.EquipmentCode}】，modulName【{db.ProcessModuleName}】,modulType【{db.ProcessModuleType}】, DB:{db.DbCode},DbStart:{db.DbStart},DbLength:{db.DbLength}");
                            continue;
                        }

                        //解析DB为Object，并添加到列表中//堆垛机返回ZJNScMessagePlcActionBO&&ZJNScMessageWcsActionBO

                        var bytesMapObjects = GlobalResource.BytesMapObjectInfos.FindAll(m => m.PlcReadDbId == db.ID);
                        var objects = await _bytesMapObjectService.ByteMapToObjectsAsync(plcConnectionId, db.ID, bytes, bytesMapObjects);
                        if (objects == null)
                        {
                            _logUtil.Error($"plcReadDbInfo:{JsonConvert.SerializeObject(db)}|将报文序列化实体，根据readDbId读取BytesMapObjectInfo表的报文配置为空...");
                            continue;
                        }

                        //初始化处理模块
                        ProcessModule processModule = null;

                        if (processModules.Exists(pm => pm.ProcessModuleName == db.ProcessModuleName && pm.ProcessModuleType == db.ProcessModuleType))
                        {
                            //即使有相同的也再复制一个相同的ProcessModule
                            processModule = processModules.Find(pm => pm.ProcessModuleName == db.ProcessModuleName && pm.ProcessModuleType == db.ProcessModuleType);
                        }
                        else
                        {
                            //没有相同则新增一个
                            processModule = new ProcessModule { ProcessModuleName = db.ProcessModuleName, ProcessModuleType = db.ProcessModuleType };
                            processModules.Add(processModule);
                        }

                        //按设备号设组报文
                        var equipmentGroup = objects.GroupBy(o => o.EquipmentCode);//归类
                        //设备分组报文添加到处理模型
                        foreach (var equipmentDbObjs in equipmentGroup)
                        {
                            EquipmentDbObject equipmentDbObject = null;
                            if (processModule.equipmentDbObjects.Exists(eo => eo.EquipmentCode == equipmentDbObjs.Key))
                            {
                                equipmentDbObject = processModule.equipmentDbObjects.Find(eo => eo.EquipmentCode == equipmentDbObjs.Key);
                                //处理模块中已存在设备报文，追加
                                equipmentDbObject.bytesObjectBases.AddRange(equipmentDbObjs.AsEnumerable());
                            }
                            else
                            {
                                equipmentDbObject = new EquipmentDbObject { EquipmentCode = equipmentDbObjs.Key };
                                equipmentDbObject.bytesObjectBases.AddRange(equipmentDbObjs.AsEnumerable());
                                //处理模块中不存在设备报文，添加
                                processModule.equipmentDbObjects.Add(equipmentDbObject);
                            }
                        }

                    }
                }
                catch (Exception ex)
                {
                    _logUtil.Error($"交互ReadPlcDbJob 读DB失败读取PLC【{plcConnectionId}】时报错ex={ex.Message}");
                }
            }

            return processModules;
        }
    }
}
